<template>
  <div class="three-wrapper">
    <div class="box" ref="box"></div>
    <div class="actions">
      <button @click="startRoam">开始场景漫游</button>
      <button @click="stopRoam">停止漫游</button>
      <button @click="resetCamera">重置相机</button>
    </div>
  </div>
</template>

<script>
import TWEEN from '@tweenjs/tween.js'
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
const clock = new THREE.Clock()
const FPS = 30 // 设置渲染频率为30FBS，也就是每秒调用渲染器render方法大约30次
const renderT = 1 / FPS // 单位秒  间隔多长时间渲染渲染一次
let timeS = 0
export default {
  components: {},
  props: {},
  data() {
    return {
      el: null,
      scene: null,
      camera: null,
      renderer: null,
      controls: null,
      animationFrameId: null,
      isRoaming: false, // 是否正在漫游
      roamTween: null, // 漫游动画实例
      curve: null // 漫游路径
    }
  },
  computed: {},
  watch: {},
  created() {},
  mounted() {
    this.el = this.$refs['box']
    this.clock = new THREE.Clock()

    this.initThree()
  },
  activated() {},
  deactivated() {},
  updated() {},
  beforeDestroy() {
    if (this.animationFrameId) {
      cancelAnimationFrame(this.animationFrameId)
    }
    this.stopRoam()
  },
  destroyed() {},
  methods: {
    initThree() {
      // 初始化 - 开始
      this.initScene() // 核心： 场景
      this.initCamera() // 核心: 相机
      this.initRender() // 核心 渲染器
      this.initLight()
      this.initControls()
      this.animate()
      this.scene.add(this.setGridHelper(100 * 2, 10 * 2))
      this.scene.add(this.setAxesHelper(100000))
      // 初始化 - 结束

      // 添加物体
      this.addMesh()
    },
    // 场景漫游
    startRoam() {
      if (this.isRoaming) return

      this.isRoaming = true
      this.controls.enabled = false // 禁用手动控制

      const p1 = new THREE.Vector3(120, 50, 120)
      const p2 = new THREE.Vector3(120, 100, -120)
      const p3 = new THREE.Vector3(-120, 50, -120)
      const p4 = new THREE.Vector3(-120, 100, 120)
      const points = [p1, p2, p3, p4, p1]

      // 创建漫游轨迹
      this.curve = new THREE.CatmullRomCurve3(points)
      const geometry = new THREE.BufferGeometry().setFromPoints(
        this.curve.getPoints(100)
      )
      const material = new THREE.LineBasicMaterial({ color: 0xff0000 })
      const curveObject = new THREE.Line(geometry, material)
      this.scene.add(curveObject)

      // 相机沿轨迹漫游
      this.startCameraRoam()
    },
    startCameraRoam() {
      let progress = { t: 0 } // 路径进度 0-1

      // 创建Tween动画
      this.roamTween = new TWEEN.Tween(progress)
        .to({ t: 1 }, 10000) // 20秒完成一圈
        .easing(TWEEN.Easing.Quadratic.InOut)
        .onUpdate((obj) => {
          console.log('obj', obj === progress) // 是相等的
          // note: progress对象的值发生变化 从0-1
          // 获取路径上的点
          const point = this.curve.getPoint(progress.t)
          const tangent = this.curve.getTangent(progress.t).normalize()

          // 更新相机位置
          this.camera.position.copy(point)

          // 计算相机朝向（看向路径前方）
          const lookAtPoint = point.clone().add(tangent.multiplyScalar(10))
          this.camera.lookAt(lookAtPoint)
        })
        .onComplete(() => {
          // 漫游结束后
          this.controls.enabled = true // 重新启用手动控制
          this.resetCamera()
          // 循环漫游
          if (this.isRoaming) {
            // this.startCameraRoam()
          }
        })
        .start()
    },
    stopRoam() {
      this.isRoaming = false
      this.controls.enabled = true // 重新启用手动控制

      if (this.roamTween) {
        this.roamTween.stop()
        this.roamTween = null
      }
    },
    resetCamera() {
      this.stopRoam()
      this.camera.position.set(0, 100, 300)
      this.camera.lookAt(this.scene.position)
      this.controls.reset()
    },
    addMesh() {
      // 添加一些建筑物或地标，增强漫游体验
      this.addBuildings()
    },
    addBuildings() {
      // 添加一些简单的建筑物
      const buildingGeometry = new THREE.BoxGeometry(10, 40, 10)
      const buildingMaterial = new THREE.MeshLambertMaterial({
        color: 0x888888
      })

      // 在场景中随机放置一些建筑物
      const positions = [
        [50, 20, 50],
        [50, 20, -50],
        [-50, 20, 50],
        [-50, 20, -50],
        [80, 25, 0],
        [-80, 25, 0],
        [0, 30, 80],
        [0, 30, -80]
      ]

      positions.forEach(pos => {
        const building = new THREE.Mesh(buildingGeometry, buildingMaterial)
        building.position.set(pos[0], pos[1], pos[2])
        this.scene.add(building)
      })

      // 添加一个中心地标
      const landmarkGeometry = new THREE.CylinderGeometry(5, 8, 60, 8)
      const landmarkMaterial = new THREE.MeshLambertMaterial({
        color: 0xff6600
      })
      const landmark = new THREE.Mesh(landmarkGeometry, landmarkMaterial)
      landmark.position.set(0, 30, 0)
      this.scene.add(landmark)
    },
    setGridHelper(size, divisions) {
      const gridHelper = new THREE.GridHelper(size, divisions)
      gridHelper.material.opacity = 0.8
      gridHelper.material.transparent = true
      return gridHelper
    },
    setAxesHelper(size) {
      return new THREE.AxesHelper(size)
    },
    // 初始化场景
    initScene() {
      this.scene = new THREE.Scene()
      // 添加背景色
      // this.scene.background = new THREE.Color(0x87ceeb) // 天蓝色背景
    },
    // 初始化相机
    initCamera() {
      const asp = this.el.offsetWidth / this.el.offsetHeight
      this.camera = new THREE.PerspectiveCamera(60, asp, 0.1, 10000000) // 增大FOV
      this.camera.position.set(0, 100, 300)
      this.camera.lookAt(this.scene.position)
      this.scene.add(this.camera)
    },
    initRender(clearColor = '#000') {
      const width = this.el.offsetWidth
      const height = this.el.offsetHeight
      this.renderer = new THREE.WebGLRenderer({
        antialias: true,
        alpha: true
      })
      this.renderer.setPixelRatio(window.devicePixelRatio)
      this.renderer.setSize(width, height)
      this.el.append(this.renderer.domElement)
    },
    initLight() {
      const ambientLight = new THREE.AmbientLight('#fff', 0.6)
      this.scene.add(ambientLight)

      const directionalLight = new THREE.DirectionalLight('#fff', 0.8)
      directionalLight.position.set(30, 50, 30).normalize()
      this.scene.add(directionalLight)

      // 添加一些点光源增强场景效果
      const pointLight1 = new THREE.PointLight(0xff4444, 0.5, 100)
      pointLight1.position.set(50, 20, 50)
      this.scene.add(pointLight1)

      const pointLight2 = new THREE.PointLight(0x4444ff, 0.5, 100)
      pointLight2.position.set(-50, 20, -50)
      this.scene.add(pointLight2)
    },
    initControls() {
      this.controls = new OrbitControls(
        this.camera,
        this.renderer.domElement,
        {}
      )
      this.controls.enableDamping = true
      this.controls.dampingFactor = 0.05
      this.controls.screenSpacePanning = true
    },
    render() {
      const dt = this.clock.getDelta()
      const T = clock.getDelta()
      timeS = timeS + T
      if (timeS > renderT) {
        // 更新Tween动画
        TWEEN.update()

        this.controls.update(dt)
        this.renderer.render(this.scene, this.camera)
        timeS = 0
      }
    },
    animate() {
      this.render()
      this.animationFrameId = requestAnimationFrame(this.animate)
    }
  },
  filters: {}
}
</script>
<style scoped lang="scss">
.three-wrapper {
  height: 100%;
  position: relative;
  .box {
    height: 100%;
    width: 100%;
    background: url(./images/map-bg.png);
    background-size: cover;
    background-repeat: no-repeat;
    overflow: hidden;
  }
  .actions {
    position: absolute;
    bottom: 10px;
    left: 10px;
    display: flex;
    gap: 10px;

    button {
      padding: 8px 16px;
      border: none;
      background-color: #409eff;
      color: #fff;
      border-radius: 6px;
      cursor: pointer;
      font-size: 14px;
      transition: all 0.3s ease;

      &:hover {
        background-color: #66b1ff;
        transform: translateY(-2px);
        box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
      }

      &:nth-child(2) {
        background-color: #f56c6c;
        &:hover {
          background-color: #f78989;
        }
      }

      &:nth-child(3) {
        background-color: #67c23a;
        &:hover {
          background-color: #85ce61;
        }
      }
    }
  }
}
</style>
